#include "MainWidget.h"
#if defined(FILE_FORMAT_JSON)
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
#endif
#include <QDebug>

MainWidget::MainWidget()
{
    //主界面利用QHBoxLayout布局为两个区域，左边区域用Left_Table_Box实现，右边由Right_Table_Box实现
    setWindowIcon(QIcon(":/logo.ico"));
    setWindowTitle("学生信息管理系统（C语言中文网出品）");
    this->resize(1000, 600);
    QHBoxLayout *HBoxLayout = new QHBoxLayout(this);

    QGroupBox * Left_Table_Box = createStuMess();
    QGroupBox * Right_Table_Box = createMenu();

    HBoxLayout->addWidget(Left_Table_Box, 4);
    HBoxLayout->addWidget(Right_Table_Box, 2);
}

//构建学生信息面板，Left_Table_Box中只包含一个QTableWidget表格控件，为了使表格尺寸自适应整个窗口，将表格添加到了QHBoxLayout布局工具中。
QGroupBox * MainWidget::createStuMess()
{
    QGroupBox * box = new QGroupBox("学生信息");
    TableWidget = new QTableWidget;
    TableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
    TableWidget->setColumnCount(7);
    TableWidget->setHorizontalHeaderLabels(QStringList() << "学号"<<"姓名"<<"年龄"<<"性别"<<"语文成绩"<<"数学成绩"<<"英文成绩");
    flushTable();
    QHBoxLayout * AutoHBoxLayout = new QHBoxLayout;
    AutoHBoxLayout->addWidget(TableWidget);
    box->setLayout(AutoHBoxLayout);

    //当用户点击某个单元格时，更新列表中显示的学生信息
    connect(TableWidget, &QTableWidget::cellClicked, this, &MainWidget::flushListWidget);
    //当用户更改某个单元格内的内容时，调用changeStuMess方法处理
    connect(TableWidget, &QTableWidget::cellChanged, this, &MainWidget::changeStuMess);
    //当用户更改某个单元格内的内容时，同时还要更新表格中显示的学生信息
    connect(TableWidget, &QTableWidget::cellChanged, this, &MainWidget::flushListWidget);
    return box;
}

//构建功能面板
//最外层有QGroupBox分组框，内部用QVBoxLayout将整个区域分为上下两部分，分别嵌入QListWidget和QGridLayout
//QGridLayout内包含添加、删除、保存、退出按钮和1个实现查找功能的单行输入框。
QGroupBox * MainWidget::createMenu()
{
    QGroupBox * box = new QGroupBox("功能面板");
    QVBoxLayout * VBoxLayout = new QVBoxLayout;

    ListWidget = new QListWidget;
    QGridLayout * Buts = new QGridLayout;
    AddStuBtn = new QPushButton("添加");
    DelStuBtn = new QPushButton("删除");
    SaveBtn = new QPushButton("保存");
    ExitBtn = new QPushButton("退出");
    FindStuEdit = new QLineEdit;
    FindStuEdit->setPlaceholderText("输入学生姓名查找...");
    FindStuEdit->setClearButtonEnabled(true);
    FindStuEdit->setStyleSheet("QLineEdit{padding:3,3,3,3;}");
    Buts->addWidget(AddStuBtn, 0, 0);
    Buts->addWidget(DelStuBtn, 0, 1);
    Buts->addWidget(FindStuEdit, 1, 0, 1, 0);
    Buts->addWidget(SaveBtn, 2, 0);
    Buts->addWidget(ExitBtn, 2, 1);

    VBoxLayout->addWidget(ListWidget, 4);
    VBoxLayout->addLayout(Buts, 2);

    box->setLayout(VBoxLayout);
    //点击添加按钮，执行addStuBox()方法
    connect(AddStuBtn, &QPushButton::clicked, this, &MainWidget::AddStuBox);
    //点击删除按钮，执行delStuFun()方法
    connect(DelStuBtn, &QPushButton::clicked, this, &MainWidget::delStuFun);
    //当向输入框内输入信息后，执行findStuMess()方法
    connect(FindStuEdit, &QLineEdit::returnPressed, this, &MainWidget::findStuMess);
    //点击退出按钮，关闭主窗口
    connect(ExitBtn, &QPushButton::clicked, this, &MainWidget::close);
    //点击保存按钮，执行saveStuMess()方法
    connect(SaveBtn, &QPushButton::clicked, this, &MainWidget::saveStuMess);

    return box;
}

//当点击添加按钮时，弹出添加学生信息的子窗口
void MainWidget::AddStuBox()
{
    messBox = new EditStuMessBox;
    //当添加学生信息窗口关闭时，更新表格，同时清空列表中显示的学生信息
    QObject::connect(messBox, &EditStuMessBox::closeBox, this, &MainWidget::flushTable);
    QObject::connect(messBox, &EditStuMessBox::closeBox, ListWidget, &QListWidget::clear);
    messBox->exec();
}

//刷新表格中的内容，当界面初始化、添加和删除学生信息后，都需要对表格内容进行更新
void MainWidget::flushTable()
{
    //更新表格内容前，要断开与cellChanged信号关联的所有槽，否则会导致程序闪退
    disconnect(TableWidget, &QTableWidget::cellChanged, 0, 0);

#if defined(FILE_FORMAT_BINARY)
    QFile file(FILE_NAME);
    file.open(QIODevice::ReadOnly);
    QDataStream dataStr(&file);
    QString id, name, age, sex, chinese_score, math_score, english_score;
    TableWidget->setRowCount(0);
    while (!dataStr.atEnd())
    {
        TableWidget->setRowCount(TableWidget->rowCount() + 1);
        dataStr >> id >> name >> age >> sex >> chinese_score >> math_score >> english_score;
        TableWidget->setItem(TableWidget->rowCount() - 1, 0, new QTableWidgetItem(id));
        TableWidget->setItem(TableWidget->rowCount() - 1, 1, new QTableWidgetItem(name));
        TableWidget->setItem(TableWidget->rowCount() - 1, 2, new QTableWidgetItem(age));
        TableWidget->setItem(TableWidget->rowCount() - 1, 3, new QTableWidgetItem(sex));
        TableWidget->setItem(TableWidget->rowCount() - 1, 4, new QTableWidgetItem(chinese_score));
        TableWidget->setItem(TableWidget->rowCount() - 1, 5, new QTableWidgetItem(math_score));
        TableWidget->setItem(TableWidget->rowCount() - 1, 6, new QTableWidgetItem(english_score));
    }
    file.close();
#elif defined(FILE_FORMAT_JSON)
    //读取JSON文件
    QFile file(FILE_NAME);
    if (!file.open(QIODevice::ReadOnly))
    {
        qDebug() << "打开文件失败";
    }

    QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
    if (!doc.isArray())
    {
        qDebug() << "类型错误";
    }

    QJsonArray student_array = doc.array();

    //遍历所有学生信息
    TableWidget->setRowCount(0);
    for (const QJsonValue &value : student_array)
    {
        QJsonObject student_obj = value.toObject();
        if (student_obj.isEmpty()) continue;

        QString id, name, age, sex, chinese_score, math_score, english_score;
        id = student_obj["id"].toString();
        name = student_obj["name"].toString();
        age = student_obj["age"].toString();
        sex = student_obj["sex"].toString();
        chinese_score = student_obj["chinese_score"].toString();
        math_score = student_obj["math_score"].toString();
        english_score = student_obj["english_score"].toString();

        TableWidget->setRowCount(TableWidget->rowCount() + 1);
        TableWidget->setItem(TableWidget->rowCount() - 1, 0, new QTableWidgetItem(id));
        TableWidget->setItem(TableWidget->rowCount() - 1, 1, new QTableWidgetItem(name));
        TableWidget->setItem(TableWidget->rowCount() - 1, 2, new QTableWidgetItem(age));
        TableWidget->setItem(TableWidget->rowCount() - 1, 3, new QTableWidgetItem(sex));
        TableWidget->setItem(TableWidget->rowCount() - 1, 4, new QTableWidgetItem(chinese_score));
        TableWidget->setItem(TableWidget->rowCount() - 1, 5, new QTableWidgetItem(math_score));
        TableWidget->setItem(TableWidget->rowCount() - 1, 6, new QTableWidgetItem(english_score));
    }
#endif

    //完成更新表格的任务后，重新关联与cellChanged相关的槽
    connect(TableWidget, &QTableWidget::cellChanged, this, &MainWidget::changeStuMess);
    connect(TableWidget, &QTableWidget::cellChanged, this, &MainWidget::flushListWidget);
}

//更新列表中显示的学生信息
void MainWidget::flushListWidget(int row)
{
    //当列表中有信息时，直接修改即可
    if (ListWidget->count() > 0)
    {
        ListWidget->item(0)->setText("学   号：" + TableWidget->item(row, 0)->text());
        ListWidget->item(1)->setText("姓   名：" + TableWidget->item(row, 1)->text());
        ListWidget->item(2)->setText("年   龄：" + TableWidget->item(row, 2)->text());
        ListWidget->item(3)->setText("性   别：" + TableWidget->item(row, 3)->text());
        ListWidget->item(4)->setText("语文成绩：" + TableWidget->item(row, 4)->text());
        ListWidget->item(5)->setText("数学成绩：" + TableWidget->item(row, 5)->text());
        ListWidget->item(6)->setText("英语成绩：" + TableWidget->item(row, 6)->text());
    }
    else
    {
        ListWidget->addItem("学   号：" + TableWidget->item(row, 0)->text());
        ListWidget->addItem("姓   名：" + TableWidget->item(row, 1)->text());
        ListWidget->addItem("年   龄：" + TableWidget->item(row, 2)->text());
        ListWidget->addItem("性   别：" + TableWidget->item(row, 3)->text());
        ListWidget->addItem("语文成绩：" + TableWidget->item(row, 4)->text());
        ListWidget->addItem("数学成绩：" + TableWidget->item(row, 5)->text());
        ListWidget->addItem("英语成绩：" + TableWidget->item(row, 6)->text());
    }
}

//删除学生信息
//思路：将除目标学生外，其他学生的信息拷贝到一个临时文件中，然后删除原来的文件，并将临时文件的文件名改为和原文件相同的名称
void MainWidget::delStuFun()
{
    QList<QTableWidgetItem*> items = TableWidget->selectedItems();
    //判断用户是否在表格中选中了某个学生信息，只有选中之后，才能执行删除操作
    if (items.count() > 0)
    {
        QMessageBox::StandardButton result = QMessageBox::question(this, "删除", "确定要删除学号为【"+items.at(0)->text()+"】的同学吗？");
        if (result == QMessageBox::Yes)
        {
#if defined(FILE_FORMAT_BINARY)
            QString ID, name, age, sex, chinese_score, math_score, english_score;
            QFile file(FILE_NAME);
            file.open(QIODevice::ReadOnly);
            QDataStream readDataStr(&file);

            QFile tempFile(TEMP_FILE_NAME);
            tempFile.open(QIODevice::WriteOnly);
            QDataStream writeDataStr(&tempFile);

            while (!readDataStr.atEnd())
            {
                readDataStr >> ID >> name >> age >> sex >> chinese_score >> math_score >> english_score;
                if (ID != items.at(0)->text())
                {
                    writeDataStr << ID << name << age << sex << chinese_score << math_score << english_score;
                }
            }
#elif defined(FILE_FORMAT_JSON)
            QFile file(FILE_NAME);
            if (!file.open(QIODevice::ReadOnly))
            {
                qDebug() << "打开文件失败";
            }

            QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
            if (!doc.isArray())
            {
                qDebug() << "类型错误";
            }

            QJsonArray student_array_temp;
            QJsonArray student_array = doc.array();
            for (const QJsonValue &value : student_array)
            {
                QJsonObject student_obj = value.toObject();

                QString id, name, age, sex, chinese_score, math_score, english_score;
                id = student_obj["id"].toString();
                name = student_obj["name"].toString();
                age = student_obj["age"].toString();
                sex = student_obj["sex"].toString();
                chinese_score = student_obj["chinese_score"].toString();
                math_score = student_obj["math_score"].toString();
                english_score = student_obj["english_score"].toString();

                //删除指定学号的学生信息
                if (id == items.at(0)->text()) continue;

                QJsonObject obj_temp;
                obj_temp.insert("id", id);
                obj_temp.insert("name", name);
                obj_temp.insert("age", age);
                obj_temp.insert("sex", sex);
                obj_temp.insert("chinese_score", chinese_score);
                obj_temp.insert("math_score", math_score);
                obj_temp.insert("english_score", english_score);
                student_array_temp.append(obj_temp);
            }

            //生成JSON文档并写入文件
            QJsonDocument doc_temp(student_array_temp);
            QFile tempFile(TEMP_FILE_NAME);
            tempFile.open(QIODevice::WriteOnly);
            tempFile.write(doc_temp.toJson(QJsonDocument::Indented));
#endif
            tempFile.close();
            file.close();
            //删除原文件，将临时文件重命名为和原文件相同的名字
            file.remove();
            tempFile.rename(FILE_NAME);
            flushTable();
            ListWidget->clear();
        }
    }
    else
    {
        QMessageBox::warning(this, "提示", "请选择要删除的学生");
    }
}

//根据输入框中的学生姓名，在表格中查找目标学生
void MainWidget::findStuMess()
{
    qint32 count = TableWidget->rowCount();
    bool findSuccess = false;
    if (count > 0)
    {
        for (qint32 i = 0; i < count; ++i)
        {
            QString name = TableWidget->item(i, 1)->text();
            if (name == FindStuEdit->text())
            {
                findSuccess = true;
                TableWidget->selectRow(i);
                flushListWidget(i);
                break;
            }
        }

        if (!findSuccess)
        {
            QMessageBox::information(this, "查找失败", "当前表格中没有【"+FindStuEdit->text()+"】同学");
        }
    }
}

//更改学生信息
//思路：逐一将学生信息拷贝到另一个临时文件中，当读取到要更改的学生信息时，将更改后的学生信息写入临时文件。最终，临时文件中存储的是更改后所有学生的信息。
//拷贝完成后，删除原文件，将临时文件的名称改为和原文件一样。
void MainWidget::changeStuMess(int row)
{
#if defined(FILE_FORMAT_BINARY)
    QString ID, name, age, sex, chinese_score, math_score, english_score;
    QString stuName = TableWidget->item(row, 1)->text();
    QFile file(FILE_NAME);
    file.open(QIODevice::ReadOnly);
    QDataStream readDataStr(&file);

    QFile tempFile(TEMP_FILE_NAME);
    tempFile.open(QIODevice::WriteOnly);
    QDataStream writeDataStr(&tempFile);

    while (!readDataStr.atEnd())
    {
        readDataStr >> ID >> name >> age >> sex >> chinese_score >> math_score >> english_score;
        if (name != stuName)
        {
            writeDataStr << ID << name << age << sex << chinese_score << math_score << english_score;
        }
        else
        {
            for (int i = 0; i < TableWidget->columnCount(); ++i)
            {
                writeDataStr << TableWidget->item(row, i)->text();
            }
        }
    }
#elif defined(FILE_FORMAT_JSON)
    QFile file(FILE_NAME);
    if (!file.open(QIODevice::ReadOnly))
    {
        qDebug() << "打开文件失败";
    }

    QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
    if (!doc.isArray())
    {
        qDebug() << "类型错误";
    }

    QString stuName = TableWidget->item(row, 1)->text();
    QJsonArray student_array_temp;
    QJsonArray student_array = doc.array();
    for (const QJsonValue &value : student_array)
    {
        QJsonObject student_obj = value.toObject();

        QString id, name, age, sex, chinese_score, math_score, english_score;
        name = student_obj["name"].toString();
        if (name != stuName)
        {
            id = student_obj["id"].toString();
            age = student_obj["age"].toString();
            sex = student_obj["sex"].toString();
            chinese_score = student_obj["chinese_score"].toString();
            math_score = student_obj["math_score"].toString();
            english_score = student_obj["english_score"].toString();
        }
        else
        {
            id = TableWidget->item(row, 0)->text();
            age = TableWidget->item(row, 2)->text();
            sex = TableWidget->item(row, 3)->text();
            chinese_score = TableWidget->item(row, 4)->text();
            math_score = TableWidget->item(row, 5)->text();
            english_score = TableWidget->item(row, 6)->text();
        }

        QJsonObject obj_temp;
        obj_temp.insert("id", id);
        obj_temp.insert("name", name);
        obj_temp.insert("age", age);
        obj_temp.insert("sex", sex);
        obj_temp.insert("chinese_score", chinese_score);
        obj_temp.insert("math_score", math_score);
        obj_temp.insert("english_score", english_score);
        student_array_temp.append(obj_temp);
    }

    //生成JSON文档并写入文件
    QJsonDocument doc_temp(student_array_temp);
    QFile tempFile(TEMP_FILE_NAME);
    tempFile.open(QIODevice::WriteOnly);
    tempFile.write(doc_temp.toJson(QJsonDocument::Indented));
#endif
    tempFile.close();
    file.close();
    file.remove();
    tempFile.rename(FILE_NAME);
}

//保存学生信息，将表格中的学生信息重新存储到文件中
void MainWidget::saveStuMess()
{
#if defined(FILE_FORMAT_BINARY)
    QFile file(FILE_NAME);
    file.open(QIODevice::WriteOnly);
    QDataStream dataStr(&file);

    for (int i = 0; i < TableWidget->rowCount(); ++i)
    {
        for (int j = 0; j < TableWidget->columnCount(); ++j)
        {
            dataStr << TableWidget->item(i, j)->text();
        }
    }

    file.close();
#elif defined(FILE_FORMAT_JSON)
    QJsonArray student_array;

    for (int i = 0; i < TableWidget->rowCount(); ++i)
    {
        QString id, name, age, sex, chinese_score, math_score, english_score;

        id = TableWidget->item(i, 0)->text();
        name = TableWidget->item(i, 1)->text();
        age = TableWidget->item(i, 2)->text();
        sex = TableWidget->item(i, 3)->text();
        chinese_score = TableWidget->item(i, 4)->text();
        math_score = TableWidget->item(i, 5)->text();
        english_score = TableWidget->item(i, 6)->text();

        QJsonObject obj_temp;
        obj_temp.insert("id", id);
        obj_temp.insert("name", name);
        obj_temp.insert("age", age);
        obj_temp.insert("sex", sex);
        obj_temp.insert("chinese_score", chinese_score);
        obj_temp.insert("math_score", math_score);
        obj_temp.insert("english_score", english_score);
        student_array.append(obj_temp);
    }

    //生成JSON文档并写入文件
    QJsonDocument doc(student_array);
    QFile file(FILE_NAME);
    file.open(QIODevice::WriteOnly);
    file.write(doc.toJson(QJsonDocument::Indented));
    file.close();
#endif

    QMessageBox::information(this, "保存", "保存成功！");
}
